Join Algorithms

Operation Output

Early Materialization

定义:在 Early Materialization(,早期物化,)中,数据库会在 JOIN 操作过程中尽早地将中间结果物化(即实际存储到临时表或内存中)。这意味着在完成部分 JOIN 步骤后,数据库会立即生成一个中间结果集,并将其存储下来。

优点

缺点

Late Materialization

定义:在 Late Materialization(,晚期物化,)中,数据库会尽量延迟物化过程,直到 JOIN 操作的最后阶段才生成最终的结果集。这意味着在 JOIN 的过程中,尽可能保持数据流式处理,只在必要时才进行物化。

优点

缺点

Early Materialization & Late Materialization

Cost Analysis Criteria

Assume:

使用 IO 次数来衡量开销(,cost,)

Join VS Cross-Product

R⨝S is the most common operation and thus must be carefully optimized.

R×S followed by a selection is inefficient because the cross-product is large.

There are many algorithms for reducing join cost, but no algorithm works well in all scenarios.

Join Algorithms

Nested Loop Join

对于数据量小的时候(比如内存可以完全容纳),直接使用嵌套循环进行 Join 操作的开销通常也可接受。

Pseudo-code for R⨝S:

foreach tuple r ∈ R:  // outer table
 foreach tuple s ∈ S: // inner table
  if r and s match then emit

Cost: M + (m ∙ N)

笔记

这里的开销计算以没有页缓存为前提。

这里将表 R 看做 outer table,S 看作 inner table。

这里的 M 由外层遍历产生,为遍历 R 的每一个 tuple,需要 M 次

在读取 R 中的一个 page 之后,遍历其中的 tuple,对每个 tuple,都遍历一遍 S 中的每一个 tuple 进行比较。

遍历 S 中的 tuple 需要进行 N 次 IO,即需要读取 S 的每一个 page。

由于 R 中有 m 个 tuple,故需要遍历 m 次 S 中的 tuple。

Optimization

Example database:

R as outer table, S as inner table:

S as outer table, R as inner table:

通过使用数据量更小的表作为 outer table,可以一定程度优化 Join 计算性能。

Naive Nested Loop Join

Pseudo code:

foreach block B_R ∈ R:
 foreach block B_S ∈ S:
  foreach tuple r ∈ B_R:
   foreach tuple s ∈ B_s:
    if r and s match then emit

Cost: M + (M ∙ N)

在 Nested Loop Join 中,对每个 outer table 中的 tuple 都遍历一遍 inner table。

在 Block Nested Loop Join 中,则变为对每个 outer table 中的 page 都遍历一遍 inner table,从而减少对 inner table 遍历的次数,减少总的 IO 次数。

和 Nested Loop Join 中相同,使用数据量更小的表作为 outer table。

Optimization

假设缓存池(,buffer page,)中可以容纳 B 个 page。

Pseudo-code:

foreach B - 2 pages p_R ∈ R:
 foreach page p_S ∈ S:
  foreach tuple r ∈ B - 2 pages:
   foreach tuple s ∈ p_s:
    if r and s match then emit

Cost:

一种理想情况

如果缓存池中能够容纳 outer table,即

Cost: M + N

Block Nested Loop Join

Pseudo-code:

foreach tuple r ∈ R:
 foreach tuple s ∈ Index(r_i = s_j):
  if r and s match then emit

Assume the cost of each index probe is some constant C per tuple.

Cost: M + (m ∙ C)

注:对于一张表,比较常用的索引为 B+ 树

Index Nested Loop Join

Sort-Merge Join

Phase #1: Sort (排序阶段)

Phase #2: Merge (合并阶段)

过程

Pseudo-code:

sort R,S on join keys
cursor_R ← R_sorted, cursor_S ← S_sorted
while cursor_R and cursor_S:
 if cursor_R > cursor_S:
  increment cursor_S 
 if cursor_R < cursor_S:
  increment cursor_R
  backtrack cursor_s (if necessary)
 elif cursor_R and cursor_S match:
  emit
  increment cursor_S

Cost:

参与 Join 运算的两张表的连接属性(,join attribute,)已经被排序好。

理想情况

最坏情况即为两个关系(表)中的所有元组(行)的连接属性(,join attribute,)都具有相同的值。

在这种情况下,Sort-Merge Join 算法需要对每一个来自第一个表的元组与第二个表中的每一个元组进行比较。因为所有的连接键都是相同的,所以算法不能跳过任何一行来寻找下一个不同的键值;它必须检查每一对可能的组合。

Cost:

最坏情况和开销

Hash Join

Phase #1: Build (构建阶段)

使用哈希函数 h1,扫描 outer table,对每一个 tuple 的 join attribute 进行哈希运算后使用哈希值将该 tuple 插入哈希表。

使用线性探测(,linear probing,)哈希表往往效果更好。

Phase #2: Probe (探测阶段)

扫描 inner table,通过每一个 tuple 的 join attribute 的哈希值在哈希表中找到对应的 outer table 中的匹配项


Pseudo-code:

build hash table HT_R for R
foreach tuple s ∈ S
 output, if h_1(s) ∈ HT_R

哈希表键和值的内容

Key: The attribute(s) that the query is joining on

Value: It varies per DBMS

Optimization: Probe Filter

在 Build 阶段为 outer table 构建一个 filter,如 Bloom Filter,在对哈希表查询时,先对 filter 查询。

Simple Hash Join Algorithm

适合在哈希表无法完全存储在内存中时使用。又被称为 GRACE Hash Join

构建过程

对两个参与 Join 运算的表都使用相同的哈希函数构建各自的分块(,partitioned,)哈希表。

探测过程

将两个表对应的分块哈希表进行逐块的比较,将索引范围相同的块放在一起比较。


Edge Cases

如果在构建中,一个分块的大小大到内存无法容纳,则用另一个哈希函数递归地对其进行分块。


Cost:

Optimization: Hybrid Hash Join

对于键值倾斜(即某一特定键值出现得较为频繁)的表较为有效。

键值倾斜的情况下,特定分块会高频出现匹配项(即热点分块)。可以将热点分块暂存在内存中,并优先进行该分块的匹配。

Difficult to get to work correctly. Rarely done in practice.

Partitioned Hash Join

Hash Join Observations

The inner table can be any size.

If we know the size of the outer table, then we can use a static hash table.

If we do not know the size, then we must use a dynamic hash table or allow for overflow pages.

Summary

AlgorithmIO CostExample
Naïve Nested Loop JoinM + (m ∙ N)1.3 hours
Block Nested Loop Join0.55 seconds
Index Nested Loop JoinM + (m ∙ C)Variable
Sort-Merge JoinM + N + (sort cost)0.75 seconds
Hash Join3 ∙ (M + N)0.45 seconds

点此查看原文